/*
 * Copyright 2008 Federal Emergency Management Agency
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0 
 *    
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 *  
 */
package gov.fema.hazus.bdc.client.settings;

import gov.fema.hazus.bdc.client.common.UserQuery;
import gov.fema.hazus.bdc.client.common.UserResults;
import gov.fema.hazus.bdc.client.common.entities.AbstractClickListener;
import gov.fema.hazus.bdc.client.common.handlers.LoggingAsyncHandler;
import gov.fema.hazus.bdc.client.common.ui.Message;
import gov.fema.hazus.bdc.client.common.util.SimpleDateFormat;
import gov.fema.hazus.bdc.client.login.Login;
import gov.fema.hazus.bdc.client.services.UserServiceProxy;
import gov.fema.hazus.bdc.shared.entity.Building;
import gov.fema.hazus.bdc.shared.entity.User;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.WindowResizeListener;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.SourcesTableEvents;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.TableListener;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

public class UserList extends Composite implements WindowResizeListener {

    class UserDeleteUpdater extends LoggingAsyncHandler
    {

        public void handleFailure(Throwable caught) {
            GWT.log("Send message to UI:" + caught, null);
            Message.get().setText(caught.getMessage());
        }

        public void handleSuccess(Object result) {
            getUsers();
        }
        
    }
    
    public class UserClickListener extends AbstractClickListener
    {

        public void handleClick(Widget sender) {
            
            // the listener is a CellHTML
            CellHTML cell = (CellHTML) sender;
            
            // track the visited users
            addVisitedUsers(cell.id);
            
            // switch UI to user editor
            GWT.log("UserClickListener, cell id=" + cell.id, null);
            
            // if you are an admin OR you click on yourself, you can edit
            User loggedInUser = Login.get().getLoginUser();
            GWT.log("UserClickListener, loggedInUser id=" + loggedInUser.getId(), null);
            
            if(loggedInUser.getId().compareTo(cell.id) == 0)
            {
            	GWT.log("loggedInUser and cell id match, allow loggedInUser to edit", null);
            }
            
            if( loggedInUser.isAdmin()
            		|| (loggedInUser.getId().compareTo(cell.id) == 0))
			{
            	User user = new User();
            	user.setId(cell.id);
                UserEditor.get().getUser(user);
			} else {
				Window.alert("You do not have permission to edit other users");
			}
            
            
        }
        
    }
    
    private class AddButtonClickListener extends AbstractClickListener
    {
        public void handleClick(Widget sender) {
            Settings.get().addNewUser();
        }
    }
    
    private class DeleteButtonClickListener extends AbstractClickListener
    {
        public void handleClick(Widget sender) {
            deleteCheckedUsers();
        }
    }
    
    private class BuildingTableListener implements TableListener
    {

        public void onCellClicked(SourcesTableEvents sender, int row, int cell) {
            GWT.log("row="+row, null);
            UserList.get().getBuilding(row);
        }
        
    }

    private int nextResult = 0;
    private int maxResults = 20;
    private boolean isSearch = false;
    
    private class PreviousSearchClickListener extends AbstractClickListener
    {
        @Override
        public void handleClick(Widget sender) {
            nextResult = (nextResult - 20);
            //UserList.get().executeSearch( Search.get().getQuery() );
        }
    }
    
    private class NextSearchClickListener extends AbstractClickListener
    {
        public void handleClick(Widget sender) {
            nextResult = (nextResult + 20);
            //UserList.get().executeSearch( Search.get().getQuery() );
        }
    }
    
    private class PreviousButtonClickListener extends AbstractClickListener
    {
        public void handleClick(Widget sender) {
            nextResult = (nextResult - 20);
            GWT.log("PreviousButtonClickListener isSearch="+isSearch, null);
            if( isSearch )
            {
                //TODO: user search not yet implemented
            }
            else
            {
                getUsers();
            }
        }
        
    }
    
    private class NextButtonClickListener extends AbstractClickListener
    {

        public void handleClick(Widget sender) {
            nextResult = (nextResult + 20);
            GWT.log("NextButtonClickListener isSearch="+isSearch, null);
            if( isSearch )
            {
               //TODO: Cannot yet search for admin users
            }
            else
            {
            	getUsers();
            }
        }
        
    }    

    public PaginationControls paginationControls;
    
    private class UserListHandler extends LoggingAsyncHandler
    {

        @Override
        public void handleFailure(Throwable caught) {
           GWT.log("handle user list failure" + caught, null);
        }

        @Override
        public void handleSuccess(Object result) {
            
        	GWT.log("handle user list success", null);
            
            Message.get().hide();
            UserResults userResults = (UserResults) result;
            UserQuery query = userResults.getUserQuery();
            
            ArrayList<User> users = userResults.getUsers();
            
            setUsers(users);
            
            GWT.log("users=" + users, null);
            
            int first = userResults.getUserQuery().getNext();
            first++;
            paginationControls.setFirst(Integer.toString(first));
            
            int last = first + userResults.getUserQuery().getMax() - 1;
            paginationControls.setLast(Integer.toString(last));
            
            int total = userResults.getUserQuery().getTotal();
            paginationControls.setTotal(Integer.toString(total));
            
            // handle if total is less than last...
            if( total < last)
            {
            	last = total;
            }
            
            // don't go off the end
            if( last >= total)
            {
                last = total;
                paginationControls.setLast(Integer.toString(last));
                paginationControls.hideNext();
            }
            else
            {
                paginationControls.showNext();
            }
            
            // don't go off the beginning
            if( first <= 1)
            {
                paginationControls.hidePrevious();
            }
            else
            {
                paginationControls.showPrevious();
            }          
            

            
            GWT.log("We have "+first+" to "+last+" of "+total+" buildings", null);

            // toggle the color of the UI
            toggleUISearchColor(isSearch);
            
            
            // do the right thing with the highlight on the left nav
            if( isSearch )
            {
                //LeftNavigation.get().highlightLeftNav("NONE");
            }
            else
            {
                //LeftNavigation.get().highlightLeftNav(constants.leftnavListBuildings());
            }
            
            // redraw the table
            redrawTable(users);

            // show the building list
            // Controller.get().mainShowBuildingList();
            
        }
        
    }
    
    // widgets
    FlexTable table = new FlexTable();
    
    
    // handlers
    private UserListHandler userListHandler = new UserListHandler();
    private UserClickListener userClickListener = new UserClickListener();

    private ArrayList<User> users;
    private ArrayList<CheckBox> checkBoxes = new ArrayList<CheckBox>();
    private HashMap<Long, Long> visited = new HashMap<Long, Long>();
    private NextButtonClickListener nextButtonClickListener;
    private PreviousButtonClickListener previousButtonClickListener;
    private SimpleDateFormat formatter;
    private DockPanel docker;
    private HorizontalPanel northButtonBar;
    private HTML serverMessagePlaceholder;
    private HorizontalPanel southButtonBar;
	private TabPanel tabPanel;
	private PreviousSearchClickListener previousSearchClickListener;
	private SettingsConstants constants;
	private Button deleteButtonBottom;
	private Button deleteButtonTop;
	private Button addButton;
    
    /**
     * Set up instance for Login
     */
    private static UserList singleton;
    public static UserList get()
    {
        if( singleton == null )
        {
            singleton = new UserList();
        }
        return singleton;
    }
    
    public void getBuilding(int row) {
        if( users != null )
        {
            if( users.size() >= row )
            {
                User clickedUser = users.get(row);
                UserEditor.get().getUser(clickedUser);
            }
        }
    }

    public void setUsers(ArrayList<User> users) {
        this.users = users;
    }

    public UserList()
    {
        
        singleton = this;
        
        // constants
        constants = (SettingsConstants)GWT.create(SettingsConstants.class);
        
        // tracker
        visited = new HashMap<Long, Long>();
        checkBoxes = new ArrayList<CheckBox>();
        
        // components
        docker  = new DockPanel();
        tabPanel = new TabPanel();
        table = new FlexTable();
        
        
        // pagination listeners
        nextButtonClickListener = new NextButtonClickListener();
        previousButtonClickListener = new PreviousButtonClickListener();
        new NextSearchClickListener();
        previousSearchClickListener = new PreviousSearchClickListener();
        
        // setup pagination with default button listeners
        paginationControls = new PaginationControls(nextButtonClickListener, previousButtonClickListener);
        
        // add the table to the docker
        docker.add(table, DockPanel.CENTER);
        docker.setCellVerticalAlignment(table, HasVerticalAlignment.ALIGN_TOP);
        docker.setCellHeight(table, "100%");
        
        // north button bar
        northButtonBar = new HorizontalPanel();
        northButtonBar.setStyleName("north-button-bar");
        
        // delete buttons
        deleteButtonTop = new Button();
        deleteButtonTop.setStyleName("incast-command-button");
        deleteButtonTop.setText("Delete");
        deleteButtonTop.addClickListener(new DeleteButtonClickListener());
        northButtonBar.add(deleteButtonTop);
        
        // add button
        addButton = new Button();
        addButton.setStyleName("incast-command-button");
        addButton.setText("Add");
        addButton.addClickListener(new AddButtonClickListener());
        northButtonBar.add(addButton);
        
        
        // error placeholder
        serverMessagePlaceholder = new HTML();
        
        northButtonBar.add(serverMessagePlaceholder);
        northButtonBar.add(paginationControls);
        northButtonBar.setCellHorizontalAlignment(paginationControls, HasHorizontalAlignment.ALIGN_RIGHT);
        
        // south button bar
        southButtonBar = new HorizontalPanel();
        southButtonBar.setStyleName("south-button-bar");     

        deleteButtonBottom = new Button();
        deleteButtonBottom.setStyleName("incast-command-button");
        deleteButtonBottom.setText("Delete");
        deleteButtonBottom.addClickListener(new DeleteButtonClickListener());
        southButtonBar.add(deleteButtonBottom);
        
        FlexTable southTable = new FlexTable();
        southTable.setWidth("100%");
        southTable.setWidget(0, 0, new HTML(""));
        southButtonBar.add(southTable);
        southButtonBar.setCellWidth(southTable, "100%");
        southButtonBar.setCellHorizontalAlignment(southTable, HasHorizontalAlignment.ALIGN_RIGHT);
        
        docker.add(northButtonBar, DockPanel.NORTH);
        docker.add(southButtonBar, DockPanel.SOUTH);
        docker.setCellWidth(southButtonBar, "100%");
        docker.setVerticalAlignment(HasVerticalAlignment.ALIGN_TOP);

        docker.setStyleName("docker-building-detail");
        
        setControls();
        
        initWidget(docker);
                
    }
    
    public void setControls()
    {
    	User loggedInUser = Login.get().getLoginUser();
    	
    	/*
    	 * Regular admins can add users, but not delete them
    	 */
    	if( loggedInUser.isAdmin() )
        {
        	deleteButtonBottom.setEnabled(false);
        	deleteButtonTop.setEnabled(false);
        	addButton.setEnabled(true);
        } else {
        	deleteButtonBottom.setEnabled(false);
        	deleteButtonTop.setEnabled(false);
        	addButton.setEnabled(false);
        }
        
    	/*
    	 * For now, the tight restriction is that only
    	 * the superadmin user gets to delete any users.
    	 * This restriction may relax with future enhancements.
    	 */
        if( loggedInUser.getUsername().equalsIgnoreCase("superadmin"))
        {
        	deleteButtonBottom.setEnabled(true);
        	deleteButtonTop.setEnabled(true);
        	addButton.setEnabled(true);
        }
        
       if( users != null )
       {
    	   redrawTable();
       }
    }
    
    public void getUsers()
    {

        UserQuery userQuery = new UserQuery();
        userQuery.setMax(maxResults);
        userQuery.setAscending(true);
        userQuery.setNext(nextResult);
        UserServiceProxy.get().find(userQuery, userListHandler);
        
    }
    
    public void redrawTable()
    {
    	redrawTable(this.users);
    }
    
    private void redrawTable(ArrayList<User> users)
    {
        table.setVisible(true);
        
        
        
        // from the end, remove each row
        for (int i=table.getRowCount()-1; i>=0;i--){
            table.removeRow(i);
        }
        // and clear the widgets
        table.clear();
        
        table.setStyleName("building-list-table");
        checkBoxes.clear();
        
        int row = 0;
        for (Iterator<User> iterator = users.iterator(); iterator.hasNext();) {
            
            User user = (User) iterator.next();

            /*
             * Skip the rendering of the superadmin user in the userlist
             * unless we are logged in as the superadmin (so we can change
             * the superadmin password)
             */
            User loggedInUser = Login.get().getLoginUser();
            if( !loggedInUser.getUsername().equalsIgnoreCase("superadmin")
            		&& user.getUsername().equalsIgnoreCase("superadmin"))
            {
            	continue;
            }
            
            
            String oneLiner = "";
            //oneLiner += "<b>"+building.getName()+"</b>";
            oneLiner += " (# "+user.getUsername()+") ";
            
            
            // build up the cells
            String userId = null;
            if( user.getId() != null )
            {
            	userId = user.getId().toString();
            }
            CheckBox userCheckBox = new CheckBox();
            GWT.log("userId for checkbox name=" + userId, null);
            userCheckBox.setName(userId);
            checkBoxes.add(userCheckBox);
            
            CellHTML userName = new CellHTML(user.getId(), user.getUsername());
            
            if( visited.containsKey(user.getId()))
            {
                
                table.getRowFormatter().setStyleName(row, "building-list-item-visited");
                userName.setStyleName("bldg-name-visited");
            
            }
            else
            {
                
                table.getRowFormatter().setStyleName(row, "building-list-item");
                userName.setStyleName("bldg-name");
            }
            
            table.getRowFormatter().setVerticalAlign(row, HasVerticalAlignment.ALIGN_MIDDLE);
            table.setWidget(row, 0, userCheckBox);
            
            table.setWidget(row, 1, userName);

            
            paginationControls.setVisible(true);
            
            // increment the row
            row++;

        }
        
        table.setVisible(true);
        
    }
    
    
    
    public class CellHTML extends HTML
    {
        public Long id;
        
        public CellHTML(Long id, String text)
        {
            this.id = id;
            this.setText(text);
			this.addClickListener(new UserClickListener());
            
        }
    }    
    
    private void deleteCheckedUsersOK()
    {
        ArrayList<User> users = new ArrayList<User>();
        User user;
        String idStr;
    
        /*
		 * Run through this.users (should be page size only)
		 * and make a map of <ID, currentUser> to test against
		 * when we go to delete them
		 */
		HashMap<Long, Boolean> adminUsers = new HashMap<Long, Boolean>();
		for (Iterator iterator = this.users.iterator(); iterator.hasNext();) {
			User u = (User) iterator.next();
			adminUsers.put(u.getId(), u.isAdmin());
		}
		
		User loggedInUser = Login.get().getLoginUser();
		
        // walk through the checkboxes array
        for (Iterator<CheckBox> iter = checkBoxes.iterator(); iter.hasNext();) {
            CheckBox cb = (CheckBox) iter.next();
            if(cb.isChecked())
            {
                // the checkbox name is the building id
                idStr = cb.getName();
                Long id = Long.parseLong(idStr);
                
                // create a building to delete
                user = new User();
                
                // set the id
                user.setId(id);
                
                /*
                 * superadmin user cannot delete themselves, 
                 * but can delete anyone else.
                 * 
                 * no one else can delete any user
                 */
                boolean isAdmin = adminUsers.get(id); 
                GWT.log("id("+id+"), isAdmin("+isAdmin+")", null);
                if( !user.getUsername().equalsIgnoreCase("superadmin"))
                {
                	// add user to delete list
                	users.add(user);
                } else {
                	GWT.log("superadmin cannot delete themselves", null);
                }                
            }
        }
        
        // start pass the list of buildings to be deleted to the api
        // BuildingDeleteUpdater will refetch the buildings
        GWT.log("users to delete=" + users, null);
        UserServiceProxy.get().deleteUsers(users, new UserDeleteUpdater());
    }
    
    private void uncheckBuildings()
    {
        for (Iterator<CheckBox> iter = checkBoxes.iterator(); iter.hasNext();) {
            CheckBox cb = (CheckBox) iter.next();
            if( cb.isChecked() )
            {
                cb.setChecked(false);
            }
        }
    }
    
    public void deleteCheckedUsers() {
        
        final DialogBox box = new DialogBox();
        
        // vpanel to hold everything
        VerticalPanel vp = new VerticalPanel();
        vp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
        
        // add the message
        HTML deleteMessagae = new HTML("Are you sure you want to delete the selected users?");
        deleteMessagae.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
        vp.add(deleteMessagae);
        
        box.setText("Warning");
        box.setWidth("200");
        
        // add buttons
        Button dontDelete = new Button("Cancel");
        dontDelete.addClickListener(new ClickListener(){

            public void onClick(Widget sender) {
                box.hide();
                uncheckBuildings();
            }
            
        });
        
        Button okDelete = new Button("OK");
        okDelete.addClickListener(new ClickListener(){

            public void onClick(Widget sender) {
                box.hide();
                deleteCheckedUsersOK();
            }
            
        });
        
        HorizontalPanel buttons = new HorizontalPanel();
        buttons.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
        
        buttons.add(okDelete);
        buttons.add(dontDelete);
        
        vp.add(buttons);
        
        
        box.add(vp);
        
        box.center();
        box.show();
        
 
    }
 
    public static void actionWrapperResetAndExecuteSearch()
    {
    	//TODO: investigate if needed for users
    	//UserList.get().resetAndExecuteSearch(Search.get().getQuery());
    }
    
    public void resetAndExecuteSearch(String query) {
        nextResult = 0;
        maxResults = 20;
        executeSearch(query);
    }   
    
    public void executeSearch(String searchTerm) {
    	//TODO: investigate if needed for users
//        isSearch = true;
//        GWT.log("executing search with query=" + searchTerm, null);
//        BuildingQuery query = new BuildingQuery();
//        query.setNext(nextResult);
//        query.setMax(maxResults);
//        query.setSearchTerm(searchTerm);
//        UserServiceProxy.get().search(query, userListHandler);
    }
    
    public void toggleUISearchColor(boolean search)
    {

        if( search )
        {
            docker.setStyleName("docker-building-detail-search");
            northButtonBar.setStyleName("north-button-bar-search");
            southButtonBar.setStyleName("south-button-bar-search");
            
            //UIController.get().setSearchBorder(true);
            //LeftNavigation.get().highlightLeftNav("NONE");
            
        }
        else
        {
            docker.setStyleName("docker-building-detail");
            northButtonBar.setStyleName("north-button-bar");
            southButtonBar.setStyleName("south-button-bar");
            
            //UIController.get().setSearchBorder(false);
            //LeftNavigation.get().highlightLeftNav(constants.leftnavListBuildings());            
        }
        
        
    }

    public void setSearchFlag(boolean b) {
        isSearch = b;
    }

    public void resetAndGetUsers() {
        nextResult = 0;
        maxResults = 20;
        getUsers();
    }

    public void addVisitedUsers(Long id) {
        visited.put(id, id);
    }

    /*
     * ActionWrappers are called by the Controller, which
     * must call static methods and cannot call instance
     * methods directly. Thus, we wrap an singleton method
     * within a static method.
     */

    private static void actionWrapperGetUsers()
    {
    	//UserList.get().setSearchFlag(false);
    	UserList.get().resetAndGetUsers();
    }

	public void onWindowResized(int width, int height) {
		GWT.log("UserList resize", null);
	}
}
